package io.union_pay.client;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.union_pay.config.UnionPayProperties;
import io.union_pay.enums.PayWay;
import io.union_pay.exception.UnionPayApiRequestException;
import io.union_pay.params.close.CloseOrderParams;
import io.union_pay.params.query.QueryOrderParams;
import io.union_pay.params.refund.RefundParams;
import io.union_pay.params.refund.RefundQueryParams;
import io.union_pay.params.unified.UnifiedOrderParams;
import io.union_pay.response.*;
import io.union_pay.utils.DigestUtils;
import io.union_pay.utils.OkHttpUtils;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.UUID;

/**
 * 银联支付接口工具类
 */
public class UnionPayClient {

    private UnionPayProperties properties;

    private final String UNIFIED_WX_ORDER = "/v1/netpay/wx/unified-order";

    private final String UNIFIED_ALI_ORDER = "/v1/netpay/trade/create";

    private final String UNIFIED_UAC_ORDER = "/v1/netpay/uac/mini-order";

    private final String QUERY_ORDER = "/v1/netpay/query";

    private final String REFUND_ORDER = "/v1/netpay/refund";

    private final String REFUND_QUERY = "/v1/netpay/refund-query";

    private final String CLOSE_ORDER = "/v1/netpay/close";


    public CloseOrderResponse closeOrder(CloseOrderParams params) {
        try {
            String requestBody = JSON.toJSONString(params);
            final String authorization = getOpenBodySig(properties.getAppid(), properties.getAppkey(), requestBody);
            Response response = OkHttpUtils.postJson(properties.getUrl() + CLOSE_ORDER, requestBody, new HashMap<String, Object>(){
                {
                    put("Authorization", authorization);
                }
            }, 10);

            if (response != null && response.code() == 200) {
                ResponseBody body = response.body();
                JSONObject json = null;

                if (body != null && (json = JSON.parseObject(body.string())) != null) {
                    CloseOrderResponse resp = JSON.toJavaObject(json, CloseOrderResponse.class);
                    return resp;
                } else {
                    throw new UnionPayApiRequestException("请求银联关闭订单接口失败");
                }
            } else {
                throw buildUnionPayApiRequestExceptionWithResponse(response);
            }
        } catch (Exception ex) {
            throw new UnionPayApiRequestException("请求银联关闭订单接口失败");
        }
    }

    /**
     * 微信退款
     */
    public RefundQueryResponse refundQuery(RefundQueryParams params) {
        try {
            String requestBody = JSON.toJSONString(params);
            final String authorization = getOpenBodySig(properties.getAppid(), properties.getAppkey(), requestBody);
            Response response = OkHttpUtils.postJson(properties.getUrl() + REFUND_QUERY, requestBody, new HashMap<String, Object>(){
                {
                    put("Authorization", authorization);
                }
            }, 10);

            if (response != null && response.code() == 200) {
                ResponseBody body = response.body();
                JSONObject json = null;

                if (body != null && (json = JSON.parseObject(body.string())) != null) {
                    RefundQueryResponse resp = JSON.toJavaObject(json, RefundQueryResponse.class);
                    return resp;
                } else {
                    throw new UnionPayApiRequestException("请求银联退款查询接口失败");
                }
            } else {
                throw buildUnionPayApiRequestExceptionWithResponse(response);
            }
        } catch (Exception ex) {
            throw new UnionPayApiRequestException("请求银联退款查询接口失败");
        }
    }

    /**
     * 微信退款
     */
    public RefundResponse refund(RefundParams params) {
        try {
            String requestBody = JSON.toJSONString(params);
            final String authorization = getOpenBodySig(properties.getAppid(), properties.getAppkey(), requestBody);
            Response response = OkHttpUtils.postJson(properties.getUrl() + REFUND_ORDER, requestBody, new HashMap<String, Object>(){
                {
                    put("Authorization", authorization);
                }
            }, 10);

            if (response != null && response.code() == 200) {
                ResponseBody body = response.body();
                JSONObject json = null;

                if (body != null && (json = JSON.parseObject(body.string())) != null) {
                    RefundResponse resp = JSON.toJavaObject(json, RefundResponse.class);
                    return resp;
                } else {
                    throw new UnionPayApiRequestException("请求银联退款接口失败");
                }
            } else {
                throw buildUnionPayApiRequestExceptionWithResponse(response);
            }
        } catch (Exception ex) {
            throw new UnionPayApiRequestException("请求银联退款接口失败");
        }
    }

    /**
     * 查询订单接口
     * @param params
     * @return
     */
    public QueryOrderResponse queryOrder(QueryOrderParams params) {
        try {
            String requestBody = JSON.toJSONString(params);
            final String authorization = getOpenBodySig(properties.getAppid(), properties.getAppkey(), requestBody);
            Response response = OkHttpUtils.postJson(properties.getUrl() + QUERY_ORDER, requestBody, new HashMap<String, Object>(){
                {
                    put("Authorization", authorization);
                }
            }, 10);
            if (response != null && response.code() == 200) {
                ResponseBody body = response.body();
                JSONObject json = null;

                if (body != null && (json = JSON.parseObject(body.string())) != null) {
                    QueryOrderResponse resp = JSON.toJavaObject(json, QueryOrderResponse.class);
                    return resp;
                } else {
                    throw new UnionPayApiRequestException("请求银联微信订单查询接口失败");
                }
            } else {
                throw buildUnionPayApiRequestExceptionWithResponse(response);
            }
        } catch (Exception ex) {
            throw new UnionPayApiRequestException("请求银联微信订单查询接口失败");
        }
    }

    /**
     * 微信支付
     * @param params
     * @param params
     * @return
     */
    public UnifiedOrderResponse unifiedOrder(PayWay payWay, UnifiedOrderParams params) {
        try {
            String requestBody = JSON.toJSONString(params);
            final String authorization = getOpenBodySig(properties.getAppid(), properties.getAppkey(), requestBody);

            String apiUrl = "";
            switch (payWay) {
                case WX_MINI:
                    apiUrl = UNIFIED_WX_ORDER;
                    break;
                case ALI_MINI:
                    apiUrl = UNIFIED_ALI_ORDER;
                    break;
                case UAC_MINI:
                    apiUrl = UNIFIED_UAC_ORDER;
                    break;
            }

            if(StringUtils.isBlank(apiUrl)) {
                throw new UnionPayApiRequestException("接口地址未配置");
            }

            Response response = OkHttpUtils.postJson(properties.getUrl() + apiUrl, requestBody, new HashMap<String, Object>(){
                {
                    put("Authorization", authorization);
                }
            }, 10);

            if (response != null && response.code() == 200) {
                ResponseBody body = response.body();
                JSONObject json = null;

                if (body != null && (json = JSON.parseObject(body.string())) != null) {
                    UnifiedOrderResponse resp = JSON.toJavaObject(json, UnifiedOrderResponse.class);
                    return resp;
                } else {
                    throw new UnionPayApiRequestException("请求银联微信下单接口失败");
                }
            } else {
                throw buildUnionPayApiRequestExceptionWithResponse(response);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new UnionPayApiRequestException("请求银联微信下单接口失败");
        }
    }

    private UnionPayApiRequestException buildUnionPayApiRequestExceptionWithResponse(Response response) {
        throw new UnionPayApiRequestException(String.format("请求失败，%s", response == null ? "没有返回" : "返回httpCode：" + response.code()));
    }


    private String getOpenBodySig(String appId, String appKey, String body) throws Exception {
        String timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());   // eg:20190227113148
        String nonce = UUID.randomUUID().toString().replace("-", ""); // eg:be46cd581c9f46ecbd71b9858311ea12
        byte[] data = body.getBytes("UTF-8");
        System.out.println("data:\n" + body);
        InputStream is = new ByteArrayInputStream(data);
        String bodyDigest = DigestUtils.sha256Hex(is); // eg:d60bc3aedeb853e2a11c0c096baaf19954dd9b752e48dea8e919e5fb29a42a8d
        System.out.println("bodyDigest:\n" + bodyDigest);
        String str1_C = appId + timestamp + nonce + bodyDigest; // eg:f0ec96ad2c3848b5b810e7aadf369e2f + 20190227113148 + be46cd581c9f46ecbd71b9858311ea12 + d60bc3aedeb853e2a11c0c096baaf19954dd9b752e48dea8e919e5fb29a42a8d

        System.out.println("str1_C:" + str1_C);
        byte[] localSignature = hmacSHA256(str1_C.getBytes(), appKey.getBytes());

        String localSignatureStr = Base64.encodeBase64String(localSignature);   // Signature
        System.out.println("Authorization:\n" + "OPEN-BODY-SIG AppId=" + "\"" + appId + "\"" + ", Timestamp=" + "\"" + timestamp + "\"" + ", Nonce=" + "\"" + nonce + "\"" + ", Signature=" + "\"" + localSignatureStr + "\"\n");
        return ("OPEN-BODY-SIG AppId=" + "\"" + appId + "\"" + ", Timestamp=" + "\"" + timestamp + "\"" + ", Nonce=" + "\"" + nonce + "\"" + ", Signature=" + "\"" + localSignatureStr + "\"");
    }

    /**
     * @param data
     * @param key
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     */
    public static byte[] hmacSHA256(byte[] data, byte[] key) throws NoSuchAlgorithmException, InvalidKeyException {
        String algorithm = "HmacSHA256";
        Mac mac = Mac.getInstance(algorithm);
        mac.init(new SecretKeySpec(key, algorithm));
        return mac.doFinal(data);
    }

    public void setProperties(UnionPayProperties properties) {
        this.properties = properties;
    }
}
